#!/usr/bin/python

from __future__ import division
import random

__metaclass__ = type

class Video:
    def __init__(self, videoId, hotness = 0, preference = 0):
        self.vid = videoId
        self.hotness = hotness
        self.preference = preference

class VideoManager:
    def __init__(self, nvideo = 300):
        #temporary var
        hot_sum_unnorm = 0.0
        for i in range(2,nvideo+2):
            hot_sum_unnorm += 1.0/i

        self.videos = []
        hot_sum = 0.0
        for i in range(2,nvideo+2):
            hotness = 1.0/i/hot_sum_unnorm
            self.videos.append(Video(i-2, hotness, hotness))

class CachePolicy:
    def __init__(self, manager, cache_rate = 0.1):
        self.filebase = manager.videos
        self.cache_rate = cache_rate
        self.reserve_files = []

    def is_cached(self, vid):
        return vid in self.cached_files

    def update_cache_rate(self, cache_rate):
        self.cache_rate = cache_rate
        

class HotFirst(CachePolicy):
    def __init__(self, manager, cache_rate = 0.1):
        super(HotFirst, self).__init__(manager, cache_rate)

    def update_cache_rate(self, cache_rate):
        self.cache_rate = cache_rate
        num_cache = int(self.cache_rate * len(self.filebase))
        self.cached_files = range(0, num_cache) 


class MultiReserve(CachePolicy):
    def __init__(self, manager, vids = [], cache_rate = 0.1):
        #CachePolicy.__init__(self, manager, cache_rate)
        super(MultiReserve, self).__init__(manager, cache_rate)
        self.reserve_files = vids
        self.update_cache_rate(cache_rate)

    def update_cache_rate(self, cache_rate):
        self.cache_rate = cache_rate
        num_cache = int(self.cache_rate * len(self.filebase))
        if len(self.reserve_files) >= num_cache:
            self.cached_files = self.reserve_files[:num_cache]
        else:
            self.cached_files = self.reserve_files
            num_cache -= len(self.reserve_files)
            vid = 0
            while True:
                if not vid in self.cached_files:
                    self.cached_files.append(vid)
                    num_cache -= 1
                vid += 1
                if num_cache <= 0 or vid >= len(self.filebase):break


class VodActions: 
    def __init__(self, manager, n_demand=180, n_demand_vip=20):
        self.filebase = manager.videos
        self.dist = []
        self.gen_dist()
        self.n_demand = n_demand
        self.n_demand_vip = n_demand_vip

    def gen_dist(self):
        hot_sum = 0.0
        #accumulation of video hotness
        self.dist = []
        for video in self.filebase:
            hot_sum += video.hotness
            self.dist.append(hot_sum)

    def get_dist(self):
        return self.dist

    def select_file(self):
        select = random.uniform(0, 1.0)
        #linear search, should be replaced by binary search
        for i in range(0, len(self.dist)):
            if self.dist[i] > select: 
                return i 

    def select_file_vip(self, policy):
        return random.choice(policy.reserve_files)

    def calc_hit(self, policy):
        nhit = 0
        for i in range(0, self.n_demand):
            if policy.is_cached(self.select_file()):
                nhit += 1

        return nhit/self.n_demand

    def calc_util(self, policy, gamma):
        util = 0
        for i in range(0, self.n_demand):
            if policy.reserve_files and i < self.n_demand_vip:
                vid = self.select_file_vip(policy)
                contrib = gamma
            else:
                vid = self.select_file()
                contrib = 1 

            if policy.is_cached(vid):
                util += contrib

        return util/self.n_demand


if __name__ == '__main__':
    manager = VideoManager(300)
    vact = VodActions(manager, 1800, 20)
    cache_rates = [0.01, 0.05, 0.1, 0.2, 0.5]
    policies = [HotFirst(manager), MultiReserve(manager, [1]), MultiReserve(manager, [100]), MultiReserve(manager, [280]), 
                MultiReserve(manager, range(260, 300)), MultiReserve(manager, range(0, 100))]
    

    #for cache_rate in cache_rates:
        #print cache_rate,
        #for policy in policies:
            #policy.update_cache_rate(cache_rate)
            #print vact.calc_hit(policy),
        #print ''

    gammas = [1, 2, 32, 64, 128]
    gammas = [128]
    for gamma in gammas: 
        #print '********************************************************'
        #print gamma

        for cache_rate in cache_rates:
            print cache_rate,
            for policy in policies:
                policy.update_cache_rate(cache_rate)
                print vact.calc_util(policy, gamma),
            print ''







        

